#include "NonPrimitives.h"
#include <stdio.h>
#include <iostream>
using namespace std;


BezierPatch::BezierPatch() {
	Gx = new Matrix4();
	Gy = new Matrix4();
	Gz = new Matrix4();
	Cx = new Matrix4();
	Cy = new Matrix4();
	Cz = new Matrix4();
	B = new Matrix4();
	U = new Vector4();
	V = new Vector4();
	Utemp = new Matrix4();
	Vtemp = new Matrix4();
	res = new Vector3();
	res1 = new Vector3();
	res2 = new Vector3();

	size = 16;
	step = 1.0 / size;
	tmp = step - (1.0 - step) / size;
	tstep = step + 2 * tmp;

	makeBezierPatch();
}

void BezierPatch::makeBezierPatch() {
	// Making B
	float val[16] = { -1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0 };
	B->set(val);

	// Making Gx,Gy,Gz
	float px[16] = { 0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100, 150, 150,
			150, 150 };
	float py[16] = { 0, 50, 100, 150, 0, 50, 100, 150, 0, 50, 100, 150, 0, 50,
			100, 150 };
	float pz[16] =
			{ 0, 0, 0, 0, 60, 60, 60, 60, -60, -60, -60, -60, 0, 0, 0, 0 };
	Gx->set(px);
	Gy->set(py);
	Gz->set(pz);

	Cx->set(B->get());
	Cx->multiply(*Gx);
	Cx->multiply(*B);

	Cy->set(B->get());
	Cy->multiply(*Gy);
	Cy->multiply(*B);

	Cz->set(B->get());
	Cz->multiply(*Gz);
	Cz->multiply(*B);

	normals = new float[size * size * 3];
	vertices = new float[size * size * 3];
	int uCount = 0;
	for (float u = 0; u < 1; u += step) {
		int vCount = 0;
		for (float v = 0; v < 1; v += step) {
			vec = x(u, v);
			int index = uCount * size * 3 + vCount * 3;
			// vertices
			vertices[index] = vec->get()[0];
			vertices[index + 1] = vec->get()[1];
			vertices[index + 2] = vec->get()[2];
			// normals
			normal(u, v);
			//res1->printVector();
			normals[index] = res1->get()[0];
			normals[index + 1] = res1->get()[1];
			normals[index + 2] = res1->get()[2];
			vCount++;
		}
		uCount++;
	}
}

Vector3* BezierPatch::normal(float u, float v) {
	U->set(3 * u * u, 2 * u, 1, 0);
	float Vvals[16] = { v * v * v, 0, 0, 0, v * v, 0, 0, 0, v, 0, 0, 0, 1, 0, 0,
			0 };
	Vtemp->set(Vvals);
	Vtemp->multiply(*Cx);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xx = V->dot(*U);
	Vtemp->set(Vvals);
	Vtemp->multiply(*Cy);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xy = V->dot(*U);
	Vtemp->set(Vvals);
	Vtemp->multiply(*Cz);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xz = V->dot(*U);
	res1->set(Xx, Xy, Xz);

	U->set(u * u * u, u * u, u, 1);
	float Nvals[16] = { 3 * v * v, 0, 0, 0, 2 * v, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
			0 };
	Vtemp->set(Nvals);
	Vtemp->multiply(*Cx);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	Xx = V->dot(*U);
	Vtemp->set(Nvals);
	Vtemp->multiply(*Cy);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	Xy = V->dot(*U);
	Vtemp->set(Nvals);
	Vtemp->multiply(*Cz);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	Xz = V->dot(*U);
	res2->set(Xx, Xy, Xz);
	res1->cross(*res1, *res2);
	res1->normalize();
	return res1;
}

Vector3* BezierPatch::x(float u, float v) {
	U->set(u * u * u, u * u, u, 1);
	float Vvals[16] = { v * v * v, 0, 0, 0, v * v, 0, 0, 0, v, 0, 0, 0, 1, 0, 0,
			0 };
	Vtemp->set(Vvals);
	Vtemp->multiply(*Cx);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xx = V->dot(*U);

	Vtemp->set(Vvals);
	Vtemp->multiply(*Cy);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xy = V->dot(*U);

	Vtemp->set(Vvals);
	Vtemp->multiply(*Cz);
	V->set(Vtemp->get()[0], Vtemp->get()[4], Vtemp->get()[8], Vtemp->get()[12]);
	float Xz = V->dot(*U);

	res->set(Xx, Xy, Xz);
	return res;
}

void BezierPatch::render() {
	t->flagTexture();
	glBegin(GL_QUADS);
	glColor3f(1, 1, 1);
	int uCount = 0;
	for (float u = 0; u < 1 - step; u += step) {
		int vCount = 0;
		for (float v = 0; v < 1 - step; v += step) {
			int index = uCount * size * 3 + vCount * 3;
			int lower_left = (uCount) * size * 3 + (vCount) * 3, lower_right =
					(uCount) * size * 3 + (vCount + 1) * 3, upper_right =
					(uCount + 1) * size * 3 + (vCount + 1) * 3, upper_left =
					(uCount + 1) * size * 3 + (vCount) * 3;
			glTexCoord2f(uCount * tstep, vCount * tstep);
			glVertex3f(vertices[upper_left], vertices[upper_left + 1],
					vertices[upper_left + 2]);
			glTexCoord2f(uCount * tstep - tstep, vCount * tstep);
			glVertex3f(vertices[lower_left], vertices[lower_left + 1],
					vertices[lower_left + 2]);
			glTexCoord2f(uCount * tstep - tstep, vCount * tstep + tstep);
			glVertex3f(vertices[lower_right], vertices[lower_right + 1],
					vertices[lower_right + 2]);
			glTexCoord2f(uCount * tstep, vCount * tstep + tstep);
			glVertex3f(vertices[upper_right], vertices[upper_right + 1],
					vertices[upper_right + 2]);

			vCount++;
		}
		uCount++;
	}
	glEnd();
}

void BezierPatch::boundingSphere(Matrix4 *c, Texture *tex) {
	t = tex;
}
